////////////////////////////////////////////////////////////////////////////////

#ifndef _BUFFER_H_
#include "buffer.h"
#endif

////////////////////////////////////////////////////////////////////////////////

#include "huffman.h"
#include <cstring>
#include <boost/scoped_array.hpp>

////////////////////////////////////////////////////////////////////////////////


Buffer::Buffer()
: m_BeginIdx(0),
  m_EndIdx(0)
{
}


////////////////////////////////////////////////////////////////////////////////


Buffer::Buffer( const char* _str , size_t _numElems )
{
    Assign(_str, _numElems);
}


////////////////////////////////////////////////////////////////////////////////


Buffer::Buffer( const Buffer& _rhs )
{
    Assign( _rhs.Data(), _rhs.Size() );
}


////////////////////////////////////////////////////////////////////////////////


const Buffer& Buffer::operator=( const Buffer& _rhs )
{
    Assign( _rhs.Data(), _rhs.Size() );
    return *this;
}


////////////////////////////////////////////////////////////////////////////////


const char* Buffer::Data() const
{
    return m_Data + m_BeginIdx;
}


////////////////////////////////////////////////////////////////////////////////


size_t Buffer::Size() const
{
    return m_EndIdx - m_BeginIdx;
}


////////////////////////////////////////////////////////////////////////////////


bool Buffer::operator==( const Buffer& _rhs ) const
{
    return Size() == _rhs.Size() && memcmp( Data(), _rhs.Data(), Size() ) == 0;
}


////////////////////////////////////////////////////////////////////////////////


bool Buffer::operator!=( const Buffer& _rhs ) const
{
    return !operator==(_rhs);
}


////////////////////////////////////////////////////////////////////////////////


void Buffer::Clear()
{
    m_BeginIdx = m_EndIdx = 0;
}


////////////////////////////////////////////////////////////////////////////////


void Buffer::Encode()
{
    if ( Size() )
    {
        // Alloc memory for encoded data
        boost::scoped_array<unsigned char>
            encoded ( new unsigned char[MAX_BUFFER_SIZE] );
        unsigned char* encodedPtr = encoded.get();
        unsigned int encodedSize = 0;

        const unsigned char* memory = 
            reinterpret_cast<const unsigned char*>(m_Data + m_BeginIdx);

        // Encode data
        huffman_encode_memory( memory,
                               Size(),
                               &encodedPtr,
                               &encodedSize );

        const char* encodedStr = reinterpret_cast<const char*>(encodedPtr);

        // Replace data with encoded data
        Assign(encodedStr, encodedSize);

        
    }
}


////////////////////////////////////////////////////////////////////////////////


void Buffer::Decode()
{
    if ( Size() )
    {
        // Alloc memory for decoded data
        boost::scoped_array<unsigned char>
            decoded ( new unsigned char[MAX_BUFFER_SIZE] );
        unsigned char* decodedPtr = decoded.get();
        unsigned int decodedSize = 0;

        unsigned char* memory =
            reinterpret_cast<unsigned char*>(m_Data + m_BeginIdx);

        // Encode data
        huffman_decode_memory( memory,
                               Size(),
                               &decodedPtr,
                               &decodedSize );

        const char* decodedStr = reinterpret_cast<const char*>(decodedPtr);

        // Replace data with decoded data
        Assign(decodedStr, decodedSize);
    }
}


////////////////////////////////////////////////////////////////////////////////


void Buffer::Assign( const char* _str, size_t _size )
{
    m_BeginIdx = m_EndIdx = 0;
    if (_str && _size)
    {
        // Number of bytes to copy
        size_t size = _size < MAX_BUFFER_SIZE ? _size : MAX_BUFFER_SIZE;
        memcpy(m_Data, _str, size);
        m_EndIdx = size;
    }
}


////////////////////////////////////////////////////////////////////////////////


void Buffer::WriteSize_t( size_t _number )
{
    WriteMemory( &_number, sizeof(size_t) );
}


////////////////////////////////////////////////////////////////////////////////


size_t Buffer::ReadSize_t()
{
    size_t number;
    ReadMemory( &number, sizeof(size_t) );
    return number;
}


////////////////////////////////////////////////////////////////////////////////


void Buffer::WriteMemory( void* _memory, size_t _size )
{
    memcpy(m_Data + m_EndIdx, _memory, _size);
    m_EndIdx += _size;
}


////////////////////////////////////////////////////////////////////////////////


void Buffer::ReadMemory( void* _memory, size_t _size )
{
    memcpy(_memory, m_Data + m_BeginIdx, _size);
    m_BeginIdx += _size;
}


////////////////////////////////////////////////////////////////////////////////


void Buffer::WriteInt( int _number )
{
    WriteMemory( &_number, sizeof(int) );
}


////////////////////////////////////////////////////////////////////////////////


int Buffer::ReadInt()
{
    int number;
    ReadMemory( &number, sizeof(int) );
    return number;
}


////////////////////////////////////////////////////////////////////////////////


void Buffer::WriteDouble( double _number )
{
    WriteMemory( &_number, sizeof(double) );
}


////////////////////////////////////////////////////////////////////////////////


double Buffer::ReadDouble()
{
    double number;
    ReadMemory( &number, sizeof(double) );
    return number;
}


////////////////////////////////////////////////////////////////////////////////


void Buffer::WriteFloat( float _number )
{
    WriteMemory( &_number, sizeof(float) );
}


////////////////////////////////////////////////////////////////////////////////


float Buffer::ReadFloat()
{
    float number;
    ReadMemory( &number, sizeof(float) );
    return number;
}


////////////////////////////////////////////////////////////////////////////////


void Buffer::WriteUInt( unsigned int _number )
{
    WriteMemory( &_number, sizeof(unsigned int) );
}


////////////////////////////////////////////////////////////////////////////////


unsigned int Buffer::ReadUInt()
{
    unsigned int number;
    ReadMemory( &number, sizeof(unsigned int) );
    return number;
}


////////////////////////////////////////////////////////////////////////////////
